#!/usr/bin/env python
# -*- coding: utf-8 -*-
import math
import random

import pyglet
from pyglet.image import Animation, AnimationFrame
from pyglet.window import key
from pyglet.window import mouse

import cocos
from cocos.layer.util_layers import ColorLayer
from cocos.sprite import Sprite
from cocos.actions import *

import pymunk as pm
from pymunk.vec2d import Vec2d

#def silly_collision_callback(shape_a, shape_b, contacts, normal_coef, data):
#    print shape_a.collision_type, shape_a.collision_type, contacts, normal_coef
#    return True

class MainGameLayer(cocos.layer.Layer):
    is_event_handler = True
    
    BLOOD_COLLISION_TYPE = 1
    BLOOD_COLLISION_GROUP = 1
    PHYSICS_TIMESTEP = 1/60.0
    
    def __init__(self):
        super( MainGameLayer, self ).__init__()
        
        # setup phisics
        self.space = pm.Space(iterations=20)
        self.space.gravity = (0.0, -1800)
        #self.space.set_default_collisionpair_func(silly_collision_callback)
        self.space.add_collisionpair_func(self.BLOOD_COLLISION_TYPE, 0, self._blood_collision_callback)
        
        self._load_resources()
        self._setup_scene()
        self._setup_characters()
        
        self.schedule_interval(self.physics_step, self.PHYSICS_TIMESTEP)
        self.schedule(self.update)
        
        self.dead_shapes = set()
    
    
    def _load_resources(self):
        img_folder = 'media\\img\\'
        load_img = pyglet.resource.image
        
        imgs = {}
        imgs['nobel-body'] = load_img(img_folder + 'nobel-body.png')
        
        imgs['nobel-face1'] = load_img(img_folder + 'nobel-face1.png')
        imgs['nobel-face2'] = load_img(img_folder + 'nobel-face2.png')
        imgs['nobel-face3'] = load_img(img_folder + 'nobel-face3.png')
        
        imgs['nobel-left_foot'] = load_img(img_folder + 'nobel-left_foot.png')
        imgs['nobel-right_foot'] = load_img(img_folder + 'nobel-right_foot.png')
        
        imgs['ground'] = load_img(img_folder + 'the_ground.png')
        imgs['seesaw'] = load_img(img_folder + 'seesaw.png')
        imgs['log'] = load_img(img_folder + 'seesaw_support.png')
        
        imgs['weight'] = load_img(img_folder + 'weight.png')
        
        imgs['blood'] = load_img(img_folder + 'blood_drop.png')
        imgs['blood-stain'] = load_img(img_folder + 'blood_stain.png')
        
        self.imgs = imgs
        
        
    def _setup_scene(self):
        ### Ground ###
        # Graphics
        ground_sprite = Sprite(self.imgs['ground'], position=(320, -50))
        self.add(ground_sprite, name='ground')
        
        p1 = ground_sprite.x - ground_sprite.width / 2, ground_sprite.y + ground_sprite.height / 2
        p2 = ground_sprite.x + ground_sprite.width / 2, ground_sprite.y + ground_sprite.height / 2
        
        # Physics
        ground_body = pm.Body(pm.inf, pm.inf)
        ground_shape = pm.Segment(ground_body, p1, p2, .0)
        ground_shape.friction = 1.0
        
        self.space.add_static(ground_shape)
        
        ### Seesaw support ###
        # Graphics
        log_sprite = Sprite(self.imgs['log'], position=(340, 60))
        self.add(log_sprite)
        
        # Physics
        log_body = pm.Body(pm.inf, pm.inf)
        log_body.position = log_sprite.position
        log_shape = pm.Circle(log_body, 29.0, (0,0))
        log_shape.friction = 1.0
        
        self.space.add_static(log_shape)
        
        ### Seesaw ###
        # Graphics
        seesaw_sprite = Sprite(self.imgs['seesaw'], position=(310, 100))
        self.add(seesaw_sprite, name='seesaw')
        
        # Phisics
        p1 = Vec2d(-seesaw_sprite.width / 2, 0)
        p2 = Vec2d(seesaw_sprite.width / 2, 0)
        
        h = Vec2d(0,2.5)
        
        verts = [p1 - h, p2 - h, p2 + h, p1 + h]
        verts.reverse()
        
        mass = 10.0
        self.seesaw_body = pm.Body(mass, pm.moment_for_poly(mass, verts, (0,0)))
        self.seesaw_body.position = seesaw_sprite.position
        
        #seesaw_shape = pm.Segment(self.seesaw_body, p1, p2, 5)
        seesaw_shape = pm.Poly(self.seesaw_body, verts, (0,0))
        seesaw_shape.friction = 1.0
        
        self.space.add(self.seesaw_body, seesaw_shape)
        
        
    def _setup_characters(self):
        ### Nobel Character
        # Graphics
        nobel_face_anim = Animation( [AnimationFrame(self.imgs['nobel-face1'], 2.0),
                                      AnimationFrame(self.imgs['nobel-face2'], .06),
                                      AnimationFrame(self.imgs['nobel-face3'], .06),
                                      AnimationFrame(self.imgs['nobel-face2'], .06)] )
        
        nobel_sprite = Sprite(self.imgs['nobel-body'], position=(250,200))
        nobel_sprite.add(Sprite(nobel_face_anim, position=(-10,30)), name='face')
        nobel_sprite.add(Sprite(self.imgs['nobel-left_foot'], position=(-20,-35)), name='l-foot')
        nobel_sprite.add(Sprite(self.imgs['nobel-right_foot'], position=(20,-35)), name='r-foot')
        
        self.add(nobel_sprite, name='nobel')
        
        # Physics
        mass = 2.0
        
        verts = [(-20, -40), (-20, 35), (20, 35), (20, -40)]
        self.nobel_body = pm.Body(mass, pm.moment_for_poly(mass, verts, (0,0)))
        self.nobel_body.position = nobel_sprite.position
        
        nobel_shape = pm.Poly(self.nobel_body, verts, (0,0))
        nobel_shape.friction = 0.2
        
        self.space.add(self.nobel_body, nobel_shape)
        
        ### Weight ###
        # Mouse-follow graphics
        weight_sprite = Sprite(self.imgs['weight'], position=(-1000, -1000))
        weight_sprite.opacity = 128
        
        self.add(weight_sprite, z=1, name='weight')
        
        
    def physics_step(self, dt):
        steps = 3
        pm_dt = self.PHYSICS_TIMESTEP / steps
        for i in range(steps):
            self.space.step(pm_dt)
            
        if self.dead_shapes:
            print self.dead_shapes
            
            for shape in self.dead_shapes:
                #shape.sprite.unschedule(self._sync_sprite_body)
                self.space.remove(shape.body, shape)
            del self.dead_shapes
            self.dead_shapes = set()
            
            
    def update(self, dt):
        self._sync_sprite_body(dt, self.get('nobel'), self.nobel_body)
        self._sync_sprite_body(dt, self.get('seesaw'), self.seesaw_body)
        
    
    def on_mouse_motion(self, x, y, dx, dy):
        self.get('weight').position = (x,y)
        
        
    def on_mouse_release(self, x, y, btn, mods):
        if btn == mouse.LEFT:
            self.spawn_weight(x, y)
        elif btn == mouse.RIGHT:
            self.spawn_blood_shower(x, y)
            
        
    def spawn_weight(self, x, y):
        weight = Sprite(self.imgs['weight'])
        self.add(weight)
        
        mass = 100.0
        verts = [(-60,64), (60,64), (80, -64), (-80,-64)]
        body = pm.Body(mass, pm.moment_for_poly(mass, verts, (0,-28)))
        body.position = x, y
        
        shape = pm.Poly(body, verts, (0,-28))
        shape.friction = 1.0
        self.space.add(body, shape)
        
        weight.schedule(self._sync_sprite_body, weight, body)
        
        
    def spawn_blood_shower(self, x, y):
        for i in range(1):
            blood_drop = Sprite(self.imgs['blood'])
            self.add(blood_drop)
            
            mass = (0.1)
            body = pm.Body(mass, pm.moment_for_circle(mass, 5, 1, (0,0)))
            body.position = x + random.randint(-10, 10) , y + random.randint(-10, 10)
            
            shape = pm.Circle(body, 5, (0,0))
            shape.friction = 1.0
            shape.group = self.BLOOD_COLLISION_GROUP
            shape.collision_type = self.BLOOD_COLLISION_TYPE
            
            shape.sprite = blood_drop
            self.space.add(body, shape)
            
            blood_drop.schedule(self._sync_sprite_body, blood_drop, body)
            
    
    def _sync_sprite_body(self, dt, sprite, body):
        sprite.position = body.position + dt * body.velocity
        sprite.rotation = - math.degrees( body.angle + dt * body.angular_velocity )
        
        
    def _blood_collision_callback(self, shape_a, shape_b, contacts, normal_coef, data):
        print shape_a.sprite, shape_a, shape_b
        self.dead_shapes.add(shape_a)
        return True
    
    
if __name__ == '__main__':
    pm.init_pymunk()
    
    cocos.director.director.init()
    
    color_bg = ColorLayer(0, 200, 255, 255)
    game_layer = MainGameLayer()
    
    cocos.director.director.run(cocos.scene.Scene(color_bg, game_layer))
